﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPMS.Log.Repository;

namespace SPMS.Log.Core
{
    /// <summary>
    /// 异步日志记录器
    /// </summary>
    /// <remarks>用于提供以异步的方式记录日志</remarks>
    /// <history>
    /// [zanezeng]               2009/10/19 9:22    创建
    /// </history>
    public sealed class AsyncLogger : Logger
    {
        #region ---Constructor

        /// <summary>
        /// 初始化类型<see cref="AsyncLogger"/>的新实例
        /// </summary>
        /// <param name="name">日志记录器的名称</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/9/30 13:24    创建
        /// </history>
        public AsyncLogger(string name)
            : this(name, Level.All, null)
        {
        }

        /// <summary>
        /// 初始化类型<see cref="AsyncLogger"/>的新实例
        /// </summary>
        /// <param name="name">日志记录器的名称</param>
        /// <param name="threshold">日志记录器允许输出的日志级别</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/9/30 13:27    创建
        /// </history>
        public AsyncLogger(string name, Level threshold)
            : this(name, threshold, null)
        {
        }

        /// <summary>
        /// 初始化类型<see cref="AsyncLogger"/>的新实例
        /// </summary>
        /// <param name="name">日志记录器的名称</param>
        /// <param name="threshold">日志记录器允许输出的日志级别</param>
        /// <param name="repository">日志记录器所属的日志存储器</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/13 15:23    创建
        /// </history>
        public AsyncLogger(string name, Level threshold, ILoggingRepository repository)
            : base(name, threshold, repository)
        {
            //初始化工作线程
            this.IntializeWorkThread();
        }

        #endregion

        #region ---Protected Method

        /// <summary>
        /// 添加一条日志记录
        /// </summary>
        /// <param name="entry">要添加的日志记录条目</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/9 9:23    创建
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/9 9:51    创建
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/19 9:23    创建
        /// </history>
        protected override void AppendEntry(LogEntry entry)
        {
            //同步缓存
            lock (this.m_Buffers)
            {
                //添加日志条目
                this.m_Buffers.Add(entry);
            }

            //通知异步添加日志条目
            this.NotifyToAsyncAppendEntries();
        }

        /// <summary>
        /// 通知日志记录器关闭
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/19 9:53    创建
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/19 11:55    创建
        /// </history>
        protected override void OnClose()
        {
            //调用基类实现
            base.OnClose();

            //判断线程是否正在运行
            if (this.m_IsThreadAlive)
            {
                //如果线程正在运行，则设置线程退出
                this.m_IsRunning = false;
            }

            //强制写入数据
            this.AppendEntries();
        }

        #endregion

        #region ---Private Method

        /// <summary>
        /// 通知执行异步添加日志条目
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/9 17:07    创建
        /// </history>
        private void NotifyToAsyncAppendEntries()
        {
            try
            {
                //判断线程是否存活
                if (!m_IsThreadAlive)
                {
                    //如果线程已经结束，防止丢失日志强制添加
                    //同步防止线程冲入
                    lock (this.m_Buffers)
                    {
                        //同步写入日志信息
                        this.AppendEntries();
                    }
                }
            }
            catch (Exception ex)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error(ex, "启动异步日志记录器'{0}'的工作线程失败", this.Name);
                }
            }
        }

        /// <summary>
        /// 添加缓存的日志条目
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/9 17:12    创建
        /// </history>
        private void AppendEntries()
        {
            //用于记录要写入的日志条目
            LogEntry[] entries = null;

            //同步
            lock (this.m_Buffers)
            {
                //获得要提交的日志条目
                entries = this.m_Buffers.ToArray();
            }

            //判断是否有需要提交的日志条目
            if (0 == entries.Length)
            {
                //如果没有需要提交的日志，则直接返回
                return;
            }

            //用于记录当前条目是否成功写入
            bool isEntryAppended = false;

            //循环写入日志
            foreach (LogEntry entry in entries)
            {
                try
                {
                    //设置没有写入
                    isEntryAppended = false;

                    //写入日志条目
                    base.AppendEntry(entry);

                    //设置写入成功
                    isEntryAppended = true;
                }
                finally
                {
                    //判断是否成功写入，如果成功写入，则删除写入的日志条目
                    if (isEntryAppended)
                    {
                        //同步
                        lock (this.m_Buffers)
                        {
                            //删除添加成功的日志条目
                            this.m_Buffers.Remove(entry);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 异步添加缓存的日志条目
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/9 16:53    创建
        /// </history>
        private void AsyncAppendEntries()
        {
            //设置线程为活动状态
            this.m_IsThreadAlive = true;

            try
            {
                //循环直到结束运行
                while (this.IsRunning)
                {
                    try
                    {
                        //添加日志条目
                        this.AppendEntries();

                        //休眠线程
                        System.Threading.Thread.Sleep(SLEEP_TIMEOUT);
                    }
                    catch (System.Threading.ThreadInterruptedException)
                    {
                        //不处理，继续保持线程活动
                    }
                    catch (System.Threading.ThreadAbortException ex)
                    {
                        //判断是否正在执行
                        if (this.m_IsRunning)
                        {
                            //输出日志
                            if (LoggingContext.MetaLog.IsWarnEnabled)
                            {
                                LoggingContext.MetaLog.Warn(ex, "异步日志记录器'{0}'的工作线程被终止，无法记录日志", this.Name);
                            }
                        }

                        //退出
                        return;
                    }
                    catch (Exception ex)
                    {
                        //输出日志
                        if (LoggingContext.MetaLog.IsErrorEnabled)
                        {
                            LoggingContext.MetaLog.Error(ex, "异步日志记录器'{0}'在记录日志的过程中发生错误，线程继续执行", this.Name);
                        }
                    }
                }
            }
            finally
            {
                //设置线程已经结束
                this.m_IsThreadAlive = false;
            }
        }

        /// <summary>
        /// 初始化工作线程
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/6 13:23    创建
        /// </history>
        private void IntializeWorkThread()
        {
            //创建工作线程
            this.m_WorkThread = new System.Threading.Thread(AsyncAppendEntries);

            //设置线程名称
            this.m_WorkThread.Name = string.Format("AsyncLogger_{0}_Thread", this.Name);

            //设置为后台线程
            this.m_WorkThread.IsBackground = true;

            //设置正在运行
            this.m_IsRunning = true;

            //启动工作线程
            this.m_WorkThread.Start();
        }

        #endregion

        #region ---Property

        /// <summary>
        /// 获得一个值，该值用于判断是否继续运行
        /// </summary>
        /// <value>
        /// 	<c>true</c>：继续运行<br/>
        /// 	<c>false</c>：停止运行
        /// </value>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/19 12:01    创建
        /// </history>
        private bool IsRunning
        {
            get
            {
                return this.m_IsRunning;
            }
        }

        #endregion

        #region ---Field

        /// <summary>
        /// 工作线程
        /// </summary>
        private System.Threading.Thread m_WorkThread;

        /// <summary>
        /// 用于记录是否运行
        /// </summary>
        private volatile bool m_IsRunning;

        /// <summary>
        /// 用于记录线程是否存活
        /// </summary>
        private bool m_IsThreadAlive;

        /// <summary>
        /// 日志条目缓存区
        /// </summary>
        private readonly List<LogEntry> m_Buffers = new List<LogEntry>();

        /// <summary>
        /// 休眠超时
        /// </summary>
        private const int SLEEP_TIMEOUT = 50;

        #endregion
    }
}
